package com.moyuwanjia.factory;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author 摸鱼丸加~
 * @create 2022-04-19 11:39
 * BeanFactory:一个构件Bean对象的工厂
 * Bean:在计算机英语中，有可重用组件的含义。
 * JavaBean:很多时候，我们会认为 JavaBean==实体类，但是JavaBean的范围应该远大于实体类
 *          可以理解为使用java语言编写的可重用组件。
 * Bean工厂就是用来创建我们的service和dao对象的。
 *      1.如果要解耦，需要一个配置文件来配置我们的service和mapper，而不是使用new去创建实例
 *      1.1:配置的内容：①唯一标识 =>全限定类名（key=value）
 *      2.读取配置文件中的内容，以反射的方式创建实例
 *
 * 问：因为是使用反射，所以每次都是newInstance()的方式创建，从而类中的成员也就会初始化多次，形成多例对象，显然执行效率没有单例对象高，如何解决？
 *
 */
public class BeanFactory {
    // 1.定义一个properties对象
    private static Properties properties;
    /*
        4.定义一个map，用于存放创建的实例对象，我们把它称之为容器，主要用于存储已创建的对象
          如果该对象每次都重新创建，那么这样的对象实例即为多例。
     */

    private static Map<String,Object> beans;
    // 2.读取properties文件，这里使用new关键字，由此引出问题，不是要解耦吗？为啥还要用new关键字？
    // :我们所说的解耦是降低耦合性，而不是完全替代它不使用
    static {
        properties = new Properties();
        // 3.这里尽量不要使用FiLeInputStream()来读取文件流，因为web工程部署之后src就没了，相对路径无法使用，
        // 绝对路径可以使用，但是，不能保证每台电脑都有c盘d盘，所以，最好的方式是使用类加载器来加载文件流对象
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(resourceAsStream);
        // 4.实例化容器
            beans = new HashMap<String, Object>();
        // 5.取出配置文件中所有的key（这个key是一个枚举类型）
            Enumeration<Object> keys = properties.keys();
            while (keys.hasMoreElements()){
                // 6.取出key值
                String key = keys.nextElement().toString();
                // 7.根据key获取值
                String beanPath = properties.getProperty(key);
                // 8.根据值创建对象
                Object value = Class.forName(beanPath).newInstance();
                // 9.存储key和value
                beans.put(key,value);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties文件失败");
        }
    }

    /**
     *
     * @param className 配置文件中的key
     * @return key对应的实例对象
     * <p>
     *     获取单例对象
     * </p>
     */
    public static Object getSingletonBean(String className){
        return  beans.get(className);
    }

    /**
     * @param name
     * @return
     * <p>
     *       根据properties文件中配置的Bean的名称，通过反射获取bean对象
     *       该方法获取到的是多例对象
     * </p>
     */
     public static Object getCasesBean(String name){
        Object bean = null;
         String beanPath = properties.getProperty(name);
         try {
            bean = Class.forName(beanPath).newInstance(); // 每次都是一个新的对象？
         } catch (InstantiationException e) {
             e.printStackTrace();
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         }
         return bean;
     }

}
